/*
 * @author Chris
 * @since 2023/1/19 22:33
 *
 * @brief    The project will scan key matrix.
 *
 * @note     This interrupt set KeyOut, and read KeyIn in the next interrupt.
 *
 * @warning  Do not to ...
 *
 * Change Logs:
 *   Date           Author       Notes
 *   2023-01-19     Chris        the first version
 */

#include "reg51.h"

#define ADDR0 P1_0
#define ADDR1 P1_1
#define ADDR2 P1_2
#define ADDR3 P1_3
#define ENLED P1_4

#define KEY_IN_1 P2_4
#define KEY_IN_2 P2_5
#define KEY_IN_3 P2_6
#define KEY_IN_4 P2_7


// 数码管显示真值表
unsigned char __code ledTruthTable[] = {
        0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,
        0x80, 0x90, 0x88, 0x83, 0xC6, 0xA1, 0x86, 0x8E
};

unsigned char keyState[4][4] = {
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
        {1, 1, 1, 1},
};

int main() {
    unsigned char keyBackup[4][4] = {
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1},
            {1, 1, 1, 1},
    };

    ENLED = 0;  // 数码管 0 显示
    ADDR3 = 1;
    ADDR2 = 0;
    ADDR1 = 0;
    ADDR0 = 0;

    EA = 1;      // 使能总中断
    TMOD = 0x01; // 设置 T0 为模式 1
    TH0 = 0xFC;  // 为 T0 赋初值，定时 2ms
    TL0 = 0x66;

    ET0 = 1;     // 使能 T0 中断
    TR0 = 1;     // 启动 T0
    P0 = ledTruthTable[0];

    while (1) {
        for (int i = 0; i < 4; ++i) {
            for (int j = 0; j < 4; ++j) {
                if (keyState[i][j] == keyBackup[i][j]) {
                    // 按键状态无变化，不处理
                    continue;
                }

                // 检测到下降沿
                if (keyBackup[i][j] == 1) {
                    P0 = ledTruthTable[i * 4 + j];
                }
                keyBackup[i][j] = keyState[i][j];
            }
        }
    }

}

/**
 * 定时器 T0 中断服务函数
 */
void InterruptTimer0() __interrupt(1) {
    // 扫描结果
    static unsigned char keyBuf[4][4] = {
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
            {0xFF, 0xFF, 0xFF, 0xFF},
    };
    // 扫描的行
    static unsigned char scanRow = 0;

    TH0 = 0xFC;  // 为 T0 赋初值，定时 2ms
    TL0 = 0x66;

    // 读取键值
    keyBuf[scanRow][0] = (keyBuf[scanRow][0] << 1) | KEY_IN_1;
    keyBuf[scanRow][1] = (keyBuf[scanRow][1] << 1) | KEY_IN_2;
    keyBuf[scanRow][2] = (keyBuf[scanRow][2] << 1) | KEY_IN_3;
    keyBuf[scanRow][3] = (keyBuf[scanRow][3] << 1) | KEY_IN_4;

    // 判断 key 状态
    for (int i = 0; i < 4; ++i) {
        if ((keyBuf[scanRow][i] & 0x0F) == 0x0F) {
            keyState[scanRow][i] = 1;
        } else if ((keyBuf[scanRow][i] & 0x0F) == 0x00) {
            keyState[scanRow][i] = 0;
        } else {}
    }

    scanRow++;
    scanRow = scanRow & 0x03;   // 逢 4 归 0

    // 逐行扫描：清空 P2 低 4 位，然后根据扫描行来设置其值
    P2 = P2 & 0xF0 | (~(1 << (3 - scanRow)));
}